package cn.lijiajia3515.cairo.auth.modules.client;

import cn.lijiajia3515.cairo.auth.domain.mongo.ClientMongo;
import cn.lijiajia3515.cairo.auth.framework.security.oauth2.server.authorization.client.CairoRegisteredClient;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.config.ClientSettings;
import org.springframework.security.oauth2.server.authorization.config.TokenSettings;

import java.util.Collections;
import java.util.Optional;
import java.util.stream.Collectors;

public class Converter {
	public static CairoRegisteredClient convert(ClientMongo client) {
		return new CairoRegisteredClient.Builder(client.getId())
			.app(client.getAppId())
			.clientId(client.getClientId())
			.clientSecret(client.getClientSecret())
			.clientAuthenticationMethods(clientAuthenticationMethods ->
				Optional.ofNullable(client.getClientAuthenticationMethods())
					.ifPresent(methods ->
						clientAuthenticationMethods.addAll(
							methods.stream()
								.map(ClientAuthenticationMethod::new)
								.collect(Collectors.toSet())
						)
					)
			)
			.authorizationGrantTypes(authorizationGrantTypes ->
				Optional.ofNullable(client.getAuthorizationGrantTypes())
					.ifPresent(types ->
						authorizationGrantTypes.addAll(
							types.stream()
								.map(AuthorizationGrantType::new)
								.collect(Collectors.toSet())
						)
					)
			)
			.scopes(scopes -> Optional.ofNullable(client.getScopes()).ifPresent(scopes::addAll))
			.redirectUris(redirectUris -> Optional.ofNullable(client.getRedirectUris()).ifPresent(redirectUris::addAll))
			.clientSettings(
				Optional.ofNullable(client.getClientSettings())
					.map(x -> {
						final ClientSettings.Builder builder = ClientSettings.builder();
						Optional.ofNullable(x.getRequireProofKey()).ifPresent(builder::requireProofKey);
						Optional.ofNullable(x.getRequireAuthorizationConsent()).ifPresent(builder::requireAuthorizationConsent);

						return builder.build();
					}).orElse(ClientSettings.builder().build())
			)
			.tokenSettings(
				Optional.ofNullable(client.getTokenSettings())
					.map(x -> {
						final TokenSettings.Builder builder = TokenSettings.builder();
						Optional.ofNullable(x.getAccessTokenTimeToLive()).ifPresent(builder::accessTokenTimeToLive);
						Optional.ofNullable(x.getReuseRefreshTokens()).ifPresent(builder::reuseRefreshTokens);
						Optional.ofNullable(x.getRefreshTokenTimeToLive()).ifPresent(builder::refreshTokenTimeToLive);
						Optional.ofNullable(x.getIdTokenSignatureAlgorithm())
							.map(SignatureAlgorithm::from)
							.ifPresent(builder::idTokenSignatureAlgorithm);

						return builder.build();
					}).orElse(TokenSettings.builder().build()))
			.build();
	}

	public static ClientMongo convert(RegisteredClient client) {
		return ClientMongo.builder()
			.id(client.getId())
			.clientId(client.getClientId())
			.clientSecret(client.getClientSecret())
			.clientAuthenticationMethods(
				Optional.ofNullable(client.getClientAuthenticationMethods()).orElse(Collections.emptySet())
					.parallelStream()
					.map(ClientAuthenticationMethod::getValue)
					.collect(Collectors.toSet())
			)
			.authorizationGrantTypes(
				Optional.ofNullable(client.getAuthorizationGrantTypes()).orElse(Collections.emptySet())
					.stream()
					.map(AuthorizationGrantType::getValue)
					.collect(Collectors.toSet())
			)
			.scopes(Optional.ofNullable(client.getScopes()).orElse(Collections.emptySet()))
			.redirectUris(Optional.ofNullable(client.getRedirectUris()).orElse(Collections.emptySet()))
			.clientSettings(
				Optional.ofNullable(client.getClientSettings())
					.map(x ->
						ClientMongo.ClientSettings.builder()
							.requireProofKey(x.isRequireProofKey())
							.requireAuthorizationConsent(x.isRequireAuthorizationConsent())
							.build()
					).orElse(new ClientMongo.ClientSettings())
			)
			.tokenSettings(
				Optional.ofNullable(client.getTokenSettings())
					.map(x ->
						ClientMongo.TokenSettings.builder()
							.accessTokenTimeToLive(x.getAccessTokenTimeToLive())
							.refreshTokenTimeToLive(x.getRefreshTokenTimeToLive())
							.reuseRefreshTokens(x.isReuseRefreshTokens())
							.idTokenSignatureAlgorithm(x.getIdTokenSignatureAlgorithm().getName())
							.build()
					).orElse(new ClientMongo.TokenSettings())
			)
			.build();
	}
}
